﻿using Avalon.Core.Data;
using Avalon.Core.Data.Templates;
using Avalon.Core.MVC.Attributes;
using Avalon.Core.Resources;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Avalon.Core.Providers {
    public class EntityProvider : BaseProvider, IEntityProvider {
        private static List<IEntityDescriptor> _entities { get; set; }

        public EntityProvider() {
            if (_entities == null) initializeEntities();
        }

        public new void Reload() {
            initializeEntities();
        }

        public IEnumerable<IEntityDescriptor> GetEntityDescriptors() {
            return _entities;
        }

        public IEntityDescriptor GetEntityDescriptor() {
            IEntityDescriptor descriptor = _entities.FirstOrDefault(e => e.ControllerAlias == Controller);
            if (descriptor == null) {
                descriptor = _entities.FirstOrDefault(e => e.ControllerName == Controller);
            }
            return descriptor;
        }

        public IEntityDescriptor GetEntityDescriptor(string entityName) {
            IEntityDescriptor descriptor = _entities.FirstOrDefault(e => e.Name == entityName);
            if (descriptor == null) {
                descriptor = _entities.FirstOrDefault(e => e.EntityType.Name == entityName);
            }
            return descriptor;
        }

        public IEntityDescriptor GetEntityDescriptor(Type entityType) {
            return _entities.FirstOrDefault(e => e.EntityType == entityType);
        }

        private void initializeEntities() {
            _entities = new List<IEntityDescriptor>();

            initializeDataAssemblyEntities();
            initializeDbEntities();
            initializeViewModelAssemblyEntities();
        }

        private void initializeDataAssemblyEntities() {
            IRepository<AvalonEntity> repository = RepositoryConfig.Initialize<AvalonEntity>();

            LibraryProvider libProvider = new LibraryProvider();
            foreach (Assembly assembly in libProvider.GetDataModelAssemblies()) {
                foreach (Type entityType in assembly.GetTypes().Where(t => typeof(IEntity).IsAssignableFrom(t))) {
                    if (entityType.GetCustomAttribute<IgnoreEntityAttribute>(false) != null) continue;

                    AvalonEntity entity = repository.Get().FirstOrDefault(e => e.AssemblyQualifiedName == entityType.AssemblyQualifiedName);
                    if (entity == null) {
                        entity = AvalonEntity.Create(entityType, entity);
                        repository.Add(entity);
                    }
                    else {
                        AvalonEntity.Create(entityType, entity);
                    }
                }
            }

            repository.SaveChanges();
        }

        private void initializeViewModelAssemblyEntities() {
            IRepository<AvalonEntity> repository = RepositoryConfig.Initialize<AvalonEntity>();

            LibraryProvider libProvider = new LibraryProvider();
            foreach (Assembly assembly in libProvider.GetViewModelAssemblies()) {
                foreach (Type viewModelType in assembly.GetTypes().Where(t => typeof(IViewModel).IsAssignableFrom(t))) {
                    ViewModelAttribute dataModelTypeAttribute = viewModelType.GetCustomAttribute<ViewModelAttribute>(true);
                    if (dataModelTypeAttribute == null) throw new Exception(AvalonResources.EntityProvider_DataModelTypeNotImplemented);

                    IEntityDescriptor descriptor = _entities.FirstOrDefault(ed => ed.EntityType == dataModelTypeAttribute.DataModelType);
                    if (descriptor == null) throw new Exception(AvalonResources.EntityProvider_DataModelDoesNotExist);

                    AvalonEntity entity = repository.Get().FirstOrDefault(e => e.AssemblyQualifiedName == descriptor.EntityType.AssemblyQualifiedName);
                    if (entity == null) throw new Exception("Entity does not exist");

                    AvalonView view = entity.ViewModels.FirstOrDefault(v => v.AssemblyQualifiedName == viewModelType.AssemblyQualifiedName);
                    if (view == null) {
                        view = AvalonView.Create(viewModelType, view);
                        entity.ViewModels.Add(view);
                    }
                    else {
                        AvalonView.Create(viewModelType, view);
                    }
                }
            }

            repository.SaveChanges();
        }

        private void initializeDbEntities() {
            IRepository<AvalonEntity> repository = RepositoryConfig.Initialize<AvalonEntity>();
            IEnumerable<AvalonEntity> entities = repository.Get();

            foreach (AvalonEntity entity in entities) {
                IEntityDescriptor descriptor = new EntityDescriptor();
                descriptor.ControllerName = entity.ControllerName;
                descriptor.ControllerAlias = entity.ControllerAlias;
                descriptor.Description = entity.Description;
                descriptor.Information = entity.Information;
                descriptor.EntityType = Type.GetType(entity.AssemblyQualifiedName);
                descriptor.Name = entity.Name;
                _entities.Add(descriptor);
            }
        }

        public static void Reset() {
            EntityProvider._entities = null;
            new EntityProvider();
        }
    }
}
